%code top{

//
// Copyright (c) ZeroC, Inc. All rights reserved.
//

// Included first to get 'TokenContext' which we need to define YYLTYPE before flex does.
#include <Slice/GrammarUtil.h>

}

%code requires{

// Define a custom location type for storing the location (and filename) of tokens.
#define YYLTYPE Slice::TokenContext

// I must set the initial stack depth to the maximum stack depth to
// disable bison stack resizing. The bison stack resizing routines use
// simple malloc/alloc/memcpy calls, which do not work for the
// YYSTYPE, since YYSTYPE is a C++ type, with constructor, destructor,
// assignment operator, etc.
#define YYMAXDEPTH  10000
#define YYINITDEPTH YYMAXDEPTH

// Newer bison versions allow to disable stack resizing by defining yyoverflow.
#define yyoverflow(a, b, c, d, e, f, g, h) yyerror(a)

}

%code top{

// Defines the rule bison uses to reduce token locations. Bison asks that the macro should
// be one-line, and treatable as a single statement when followed by a semi-colon.
// `N` is the number of tokens that are being combined, and (Cur) is their combined location.
#define YYLLOC_DEFAULT(Cur, Rhs, N)                               \
do                                                                \
    if(N == 1)                                                    \
    {                                                             \
        (Cur) = (YYRHSLOC((Rhs), 1));                             \
    }                                                             \
    else                                                          \
    {                                                             \
        if(N)                                                     \
        {                                                         \
            (Cur).firstLine = (YYRHSLOC((Rhs), 1)).firstLine;     \
            (Cur).firstColumn = (YYRHSLOC((Rhs), 1)).firstColumn; \
        }                                                         \
        else                                                      \
        {                                                         \
            (Cur).firstLine = (YYRHSLOC((Rhs), 0)).lastLine;      \
            (Cur).firstColumn = (YYRHSLOC((Rhs), 0)).lastColumn;  \
        }                                                         \
        (Cur).filename = (YYRHSLOC((Rhs), N)).filename;           \
        (Cur).lastLine = (YYRHSLOC((Rhs), N)).lastLine;           \
        (Cur).lastColumn = (YYRHSLOC((Rhs), N)).lastColumn;       \
    }                                                             \
while(0)

}

%code{

// Forward declaration of the lexing function generated by flex, so bison knows about it.
// This must match the definition of 'yylex' (or 'slice_lex') in the generated scanner.
int slice_lex(YYSTYPE* lvalp, YYLTYPE* llocp);

}

%{

#include <IceUtil/InputUtil.h>
#include <IceUtil/UUID.h>
#include <cstring>

#ifdef _MSC_VER
// warning C4102: 'yyoverflowlab' : unreferenced label
#   pragma warning(disable:4102)
// warning C4065: switch statement contains 'default' but no 'case' labels
#   pragma warning(disable:4065)
// warning C4244: '=': conversion from 'int' to 'yytype_int16', possible loss of data
#   pragma warning(disable:4244)
// warning C4127: conditional expression is constant
#   pragma warning(disable:4127)
#endif

// Avoid old style cast warnings in generated grammar
#ifdef __GNUC__
#  pragma GCC diagnostic ignored "-Wold-style-cast"
#endif

// Avoid clang conversion warnings
#if defined(__clang__)
#   pragma clang diagnostic ignored "-Wconversion"
#   pragma clang diagnostic ignored "-Wsign-conversion"
#endif

using namespace std;
using namespace Slice;

void
slice_error(const char* s)
{
    // yacc and recent versions of Bison use "syntax error" instead
    // of "parse error".

    if (strcmp(s, "parse error") == 0)
    {
        unit->error("syntax error");
    }
    else
    {
        unit->error(s);
    }
}

%}

// Directs Bison to generate a re-entrant parser.
%define api.pure
// Specifies what type to back the tokens with (their semantic values).
%define api.value.type {Slice::GrammarBasePtr}
// Bison generates a '.hpp' file by default, but we rename it to '.h' for consistency.
// This changes the include statement to include "Grammar.h" instead of "Grammar.hpp".
%define api.header.include {"Grammar.h"}
// Enables Bison's token location tracking functionality.
%locations

// All keyword tokens. Make sure to modify the "keyword" rule in this
// file if the list of keywords is changed. Also make sure to add the
// keyword to the keyword table in Scanner.l.
%token ICE_MODULE
%token ICE_CLASS
%token ICE_INTERFACE
%token ICE_EXCEPTION
%token ICE_STRUCT
%token ICE_SEQUENCE
%token ICE_DICTIONARY
%token ICE_ENUM
%token ICE_OUT
%token ICE_EXTENDS
%token ICE_IMPLEMENTS
%token ICE_THROWS
%token ICE_VOID
%token ICE_BOOL
%token ICE_BYTE
%token ICE_SHORT
%token ICE_USHORT
%token ICE_INT
%token ICE_UINT
%token ICE_VARINT
%token ICE_VARUINT
%token ICE_LONG
%token ICE_ULONG
%token ICE_VARLONG
%token ICE_VARULONG
%token ICE_FLOAT
%token ICE_DOUBLE
%token ICE_STRING
%token ICE_OBJECT
%token ICE_CONST
%token ICE_FALSE
%token ICE_TRUE
%token ICE_IDEMPOTENT
%token ICE_TAG
%token ICE_OPTIONAL
%token ICE_ANYCLASS
%token ICE_VALUE
%token ICE_UNCHECKED

// Other tokens.
%token ICE_STRING_LITERAL
%token ICE_INTEGER_LITERAL
%token ICE_FLOATING_POINT_LITERAL
%token ICE_IDENTIFIER
%token ICE_SCOPED_IDENTIFIER
%token ICE_LOCAL_METADATA_OPEN
%token ICE_LOCAL_METADATA_CLOSE
%token ICE_FILE_METADATA_OPEN
%token ICE_FILE_METADATA_IGNORE
%token ICE_FILE_METADATA_CLOSE

// Here 'OPEN' means these tokens end with an open parenthesis.
%token ICE_IDENT_OPEN
%token ICE_KEYWORD_OPEN
%token ICE_TAG_OPEN
%token ICE_OPTIONAL_OPEN

%token BAD_CHAR

%%

// ----------------------------------------------------------------------
start
// ----------------------------------------------------------------------
: definitions
;

// ----------------------------------------------------------------------
opt_semicolon
// ----------------------------------------------------------------------
: ';'
| %empty
;

// ----------------------------------------------------------------------
file_metadata
// ----------------------------------------------------------------------
: ICE_FILE_METADATA_OPEN string_list ICE_FILE_METADATA_CLOSE
{
    $$ = $2;
}
| ICE_FILE_METADATA_IGNORE string_list ICE_FILE_METADATA_CLOSE
{
    unit->error("file metadata must appear before any definitions");
    $$ = $2; // Dummy
}
;

// ----------------------------------------------------------------------
local_metadata
// ----------------------------------------------------------------------
: ICE_LOCAL_METADATA_OPEN string_list ICE_LOCAL_METADATA_CLOSE
{
    $$ = $2;
}
| local_metadata ICE_LOCAL_METADATA_OPEN string_list ICE_LOCAL_METADATA_CLOSE
{
    StringListTokPtr metadata1 = StringListTokPtr::dynamicCast($1);
    StringListTokPtr metadata2 = StringListTokPtr::dynamicCast($3);
    metadata1->v.splice(metadata1->v.end(), metadata2->v);
    $$ = metadata1;
}
| %empty
{
    $$ = new StringListTok;
}
;

// ----------------------------------------------------------------------
definitions
// ----------------------------------------------------------------------
: definitions file_metadata
{
    StringListTokPtr metaData = StringListTokPtr::dynamicCast($2);
    if(!metaData->v.empty())
    {
        unit->addFileMetaData(metaData->v);
    }
}
| definitions local_metadata definition
{
    StringListTokPtr metaData = StringListTokPtr::dynamicCast($2);
    ContainedPtr contained = ContainedPtr::dynamicCast($3);
    if(contained && !metaData->v.empty())
    {
        contained->setMetaData(metaData->v);
    }
}
| %empty
;

// ----------------------------------------------------------------------
definition
// ----------------------------------------------------------------------
: module_def
{
    assert($1 == 0 || ModulePtr::dynamicCast($1));
}
opt_semicolon
| class_decl
{
    assert($1 == 0 || ClassDeclPtr::dynamicCast($1));
}
';'
| class_decl
{
    unit->error("`;' missing after class forward declaration");
}
| class_def
{
    assert($1 == 0 || ClassDefPtr::dynamicCast($1));
}
opt_semicolon
| interface_decl
{
    assert($1 == 0 || InterfaceDeclPtr::dynamicCast($1));
}
';'
| interface_decl
{
    unit->error("`;' missing after interface forward declaration");
}
| interface_def
{
    assert($1 == 0 || InterfaceDefPtr::dynamicCast($1));
}
opt_semicolon
| exception_decl
{
    assert($1 == 0);
}
';'
| exception_decl
{
    unit->error("`;' missing after exception forward declaration");
}
| exception_def
{
    assert($1 == 0 || ExceptionPtr::dynamicCast($1));
}
opt_semicolon
| struct_decl
{
    assert($1 == 0);
}
';'
| struct_decl
{
    unit->error("`;' missing after struct forward declaration");
}
| struct_def
{
    assert($1 == 0 || StructPtr::dynamicCast($1));
}
opt_semicolon
| sequence_def
{
    assert($1 == 0 || SequencePtr::dynamicCast($1));
}
';'
| sequence_def
{
    unit->error("`;' missing after sequence definition");
}
| dictionary_def
{
    assert($1 == 0 || DictionaryPtr::dynamicCast($1));
}
';'
| dictionary_def
{
    unit->error("`;' missing after dictionary definition");
}
| enum_def
{
    assert($1 == 0 || EnumPtr::dynamicCast($1));
}
opt_semicolon
| const_def
{
    assert($1 == 0 || ConstPtr::dynamicCast($1));
}
';'
| const_def
{
    unit->error("`;' missing after const definition");
}
| error ';'
{
    yyerrok;
}
;

// ----------------------------------------------------------------------
module_def
// ----------------------------------------------------------------------
: ICE_MODULE ICE_IDENTIFIER
{
    StringTokPtr ident = StringTokPtr::dynamicCast($2);
    ContainerPtr cont = unit->currentContainer();

    ModulePtr module;
    if (UnitPtr ut = UnitPtr::dynamicCast(cont))
    {
        module = ut->createModule(ident->v);
    }
    else if (ModulePtr mod = ModulePtr::dynamicCast(cont))
    {
        module = mod->createModule(ident->v);
    }

    if(module)
    {
        cont->checkIntroduced(ident->v, module);
        unit->pushContainer(module);
        $$ = module;
    }
    else
    {
        $$ = 0;
    }
}
'{' definitions '}'
{
    if($3)
    {
        unit->popContainer();
        $$ = $3;
    }
    else
    {
        $$ = 0;
    }
}
| ICE_MODULE ICE_SCOPED_IDENTIFIER
{
    StringTokPtr ident = StringTokPtr::dynamicCast($2);

    // Split the 'scoped identifier' into separate module names.
    vector<string> modules;
    size_t startPos = 0;
    size_t endPos;
    while((endPos = ident->v.find("::", startPos)) != string::npos)
    {
        modules.push_back(ident->v.substr(startPos, (endPos - startPos)));
        startPos = endPos + 2;
    }
    modules.push_back(ident->v.substr(startPos));

    // Create the nested modules.
    ContainerPtr cont = unit->currentContainer();
    ModulePtr parent;
    size_t i = 0;
    if (UnitPtr ut = UnitPtr::dynamicCast(cont))
    {
        parent = ut->createModule(modules[i++]);
        if (parent)
        {
            ut->checkIntroduced(ident->v, parent);
            unit->pushContainer(parent);
        }
    }

    if (i == 0 || parent)
    {
        parent = unit->currentModule();
        for (; i < modules.size(); i++)
        {
            if (ModulePtr module = parent->createModule(modules[i]))
            {
                parent->checkIntroduced(ident->v, module);
                unit->pushContainer(module);
                parent = module;
            }
            else
            {
                // If an error occurs creating one of the modules, back up the entire chain.
                for(; i > 0; i--)
                {
                    unit->popContainer();
                }
                parent = nullptr;
                break;
            }
        }
    }

    $$ = parent;
}
'{' definitions '}'
{
    if($3)
    {
        StringTokPtr ident = StringTokPtr::dynamicCast($2);
        size_t startPos = 0;
        while((startPos = ident->v.find("::", startPos + 2)) != string::npos)
        {
            unit->popContainer();
        }
        $$ = unit->currentContainer();
        unit->popContainer();
    }
    else
    {
        $$ = 0;
    }
}
;

// ----------------------------------------------------------------------
exception_id
// ----------------------------------------------------------------------
: ICE_EXCEPTION ICE_IDENTIFIER
{
    $$ = $2;
}
| ICE_EXCEPTION keyword
{
    StringTokPtr ident = StringTokPtr::dynamicCast($2);
    unit->error("keyword `" + ident->v + "' cannot be used as exception name");
    $$ = $2; // Dummy
}
;

// ----------------------------------------------------------------------
exception_decl
// ----------------------------------------------------------------------
: exception_id
{
    unit->error("exceptions cannot be forward declared");
    $$ = 0;
}
;

// ----------------------------------------------------------------------
exception_def
// ----------------------------------------------------------------------
: exception_id exception_extends
{
    StringTokPtr ident = StringTokPtr::dynamicCast($1);
    ExceptionPtr base = ExceptionPtr::dynamicCast($2);
    ModulePtr cont = unit->currentModule();
    ExceptionPtr ex = cont->createException(ident->v, base);
    if(ex)
    {
        cont->checkIntroduced(ident->v, ex);
        unit->pushContainer(ex);
    }
    $$ = ex;
}
'{' data_members '}'
{
    if($3)
    {
        unit->popContainer();
    }
    $$ = $3;
}
;

// ----------------------------------------------------------------------
exception_extends
// ----------------------------------------------------------------------
: extends scoped_name
{
    StringTokPtr scoped = StringTokPtr::dynamicCast($2);
    ContainerPtr cont = unit->currentContainer();
    ContainedPtr contained = cont->lookupException(scoped->v);
    cont->checkIntroduced(scoped->v);
    $$ = contained;
}
| %empty
{
    $$ = 0;
}
;

// ----------------------------------------------------------------------
tag
// ----------------------------------------------------------------------
: ICE_TAG_OPEN ICE_INTEGER_LITERAL ')'
{
    IntegerTokPtr i = IntegerTokPtr::dynamicCast($2);

    int tag;
    if(i->v < 0 || i->v > INT32_MAX)
    {
        unit->error("tag is out of range");
        tag = -1;
    }
    else
    {
        tag = static_cast<int>(i->v);
    }

    TaggedDefTokPtr m = new TaggedDefTok(tag);
    $$ = m;
}
| ICE_TAG_OPEN scoped_name ')'
{
    StringTokPtr scoped = StringTokPtr::dynamicCast($2);

    ContainerPtr cont = unit->currentContainer();
    assert(cont);
    ContainedList cl = cont->lookupContained(scoped->v, false);
    if(cl.empty())
    {
        EnumeratorList enumerators = cont->enumerators(scoped->v);
        if(enumerators.size() == 1)
        {
            // Found
            cl.push_back(enumerators.front());
            scoped->v = enumerators.front()->scoped();
            unit->warning(Deprecated, string("referencing enumerator `") + scoped->v
                          + "' without its enumeration's scope is deprecated");
        }
        else if(enumerators.size() > 1)
        {
            ostringstream os;
            os << "enumerator `" << scoped->v << "' could designate";
            bool first = true;
            for(const auto& p : enumerators)
            {
                if(first)
                {
                    first = false;
                }
                else
                {
                    os << " or";
                }

                os << " `" << p->scoped() << "'";
            }
            unit->error(os.str());
        }
        else
        {
            unit->error(string("`") + scoped->v + "' is not defined");
        }
    }

    if(cl.empty())
    {
        YYERROR; // Can't continue, jump to next yyerrok
    }
    cont->checkIntroduced(scoped->v);

    std::int64_t tag = -1;
    EnumeratorPtr enumerator = EnumeratorPtr::dynamicCast(cl.front());
    ConstPtr constant = ConstPtr::dynamicCast(cl.front());
    if (constant)
    {
        BuiltinPtr b = BuiltinPtr::dynamicCast(constant->type());
        if (b && b->isIntegralType())
        {
            tag = IceUtilInternal::strToInt64(constant->value().c_str(), 0, 0);
        }
    }
    else if(enumerator)
    {
        tag = enumerator->value();
    }

    if (tag < 0 || tag > INT32_MAX)
    {
        unit->error("cannot use value of `" + scoped->v + "' as a tag");
    }

    TaggedDefTokPtr m = new TaggedDefTok(static_cast<int>(tag));
    $$ = m;
}
| ICE_TAG_OPEN ')'
{
    unit->error("missing tag");
    TaggedDefTokPtr m = new TaggedDefTok; // Dummy
    $$ = m;
}
| ICE_TAG
{
    unit->error("missing tag");
    TaggedDefTokPtr m = new TaggedDefTok; // Dummy
    $$ = m;
}
;

// ----------------------------------------------------------------------
optional
// ----------------------------------------------------------------------
: ICE_OPTIONAL_OPEN ICE_INTEGER_LITERAL ')'
{
    IntegerTokPtr i = IntegerTokPtr::dynamicCast($2);
    if (!unit->compatMode())
    {
        unit->warning(Deprecated, string("The `optional' keyword is deprecated, use `tag' instead"));
    }

    int tag;
    if(i->v < 0 || i->v > INT32_MAX)
    {
        unit->error("tag is out of range");
        tag = -1;
    }
    else
    {
        tag = static_cast<int>(i->v);
    }

    TaggedDefTokPtr m = new TaggedDefTok(tag);
    $$ = m;
}
| ICE_OPTIONAL_OPEN scoped_name ')'
{
    StringTokPtr scoped = StringTokPtr::dynamicCast($2);
    if (!unit->compatMode())
    {
        unit->warning(Deprecated, string("The `optional' keyword is deprecated, use `tag' instead"));
    }

    ContainerPtr cont = unit->currentContainer();
    assert(cont);
    ContainedList cl = cont->lookupContained(scoped->v, false);
    if(cl.empty())
    {
        EnumeratorList enumerators = cont->enumerators(scoped->v);
        if(enumerators.size() == 1)
        {
            // Found
            cl.push_back(enumerators.front());
            scoped->v = enumerators.front()->scoped();
            unit->warning(Deprecated, string("referencing enumerator `") + scoped->v
                          + "' without its enumeration's scope is deprecated");
        }
        else if(enumerators.size() > 1)
        {
            ostringstream os;
            os << "enumerator `" << scoped->v << "' could designate";
            bool first = true;
            for(const auto& p : enumerators)
            {
                if(first)
                {
                    first = false;
                }
                else
                {
                    os << " or";
                }

                os << " `" << p->scoped() << "'";
            }
            unit->error(os.str());
        }
        else
        {
            unit->error(string("`") + scoped->v + "' is not defined");
        }
    }

    if(cl.empty())
    {
        YYERROR; // Can't continue, jump to next yyerrok
    }
    cont->checkIntroduced(scoped->v);

    std::int64_t tag = -1;
    EnumeratorPtr enumerator = EnumeratorPtr::dynamicCast(cl.front());
    ConstPtr constant = ConstPtr::dynamicCast(cl.front());
    if (constant)
    {
        BuiltinPtr b = BuiltinPtr::dynamicCast(constant->type());
        if(b && b->isIntegralType())
        {
            tag = IceUtilInternal::strToInt64(constant->value().c_str(), 0, 0);
        }
    }
    else if(enumerator)
    {
        tag = enumerator->value();
    }

    if (tag < 0 || tag > INT32_MAX)
    {
        unit->error("cannot use value of `" + scoped->v + "' as a tag");
    }

    TaggedDefTokPtr m = new TaggedDefTok(static_cast<int>(tag));
    $$ = m;
}
| ICE_OPTIONAL_OPEN ')'
{
    if (!unit->compatMode())
    {
        unit->warning(Deprecated, string("The `optional' keyword is deprecated, use `tag' instead"));
    }
    unit->error("missing tag");
    TaggedDefTokPtr m = new TaggedDefTok; // Dummy
    $$ = m;
}
| ICE_OPTIONAL
{
    if (!unit->compatMode())
    {
        unit->warning(Deprecated, string("The `optional' keyword is deprecated, use `tag' instead"));
    }
    unit->error("missing tag");
    TaggedDefTokPtr m = new TaggedDefTok; // Dummy
    $$ = m;
}
;

// ----------------------------------------------------------------------
struct_id
// ----------------------------------------------------------------------
: ICE_STRUCT ICE_IDENTIFIER
{
    $$ = $2;
}
| ICE_STRUCT keyword
{
    StringTokPtr ident = StringTokPtr::dynamicCast($2);
    unit->error("keyword `" + ident->v + "' cannot be used as struct name");
    $$ = $2; // Dummy
}
;

// ----------------------------------------------------------------------
struct_decl
// ----------------------------------------------------------------------
: struct_id
{
    unit->error("structs cannot be forward declared");
    $$ = 0; // Dummy
}
;

// ----------------------------------------------------------------------
struct_def
// ----------------------------------------------------------------------
: struct_id
{
    StringTokPtr ident = StringTokPtr::dynamicCast($1);
    ModulePtr cont = unit->currentModule();
    StructPtr st = cont->createStruct(ident->v);
    if(st)
    {
        cont->checkIntroduced(ident->v, st);
        unit->pushContainer(st);
    }
    else
    {
        st = cont->createStruct(IceUtil::generateUUID()); // Dummy
        assert(st);
        unit->pushContainer(st);
    }
    $$ = st;
}
'{' data_members '}'
{
    if($2)
    {
        unit->popContainer();
    }
    $$ = $2;

    //
    // Empty structures are not allowed
    //
    StructPtr st = StructPtr::dynamicCast($$);
    assert(st);
    if (!st->hasDataMembers())
    {
        unit->error("struct `" + st->name() + "' must have at least one member"); // $$ is a dummy
    }
}
;

// ----------------------------------------------------------------------
class_name
// ----------------------------------------------------------------------
: ICE_CLASS ICE_IDENTIFIER
{
    $$ = $2;
}
| ICE_CLASS keyword
{
    StringTokPtr ident = StringTokPtr::dynamicCast($2);
    unit->error("keyword `" + ident->v + "' cannot be used as class name");
    $$ = $2; // Dummy
}
;

// ----------------------------------------------------------------------
class_id
// ----------------------------------------------------------------------
: ICE_CLASS ICE_IDENT_OPEN ICE_INTEGER_LITERAL ')'
{
    IceUtil::Int64 id = IntegerTokPtr::dynamicCast($3)->v;
    if(id < 0)
    {
        unit->error("invalid compact id for class: id must be a positive integer");
    }
    else if(id > INT32_MAX)
    {
        unit->error("invalid compact id for class: value is out of range");
    }
    else
    {
        string typeId = unit->getTypeId(static_cast<int>(id));
        if(!typeId.empty())
        {
            unit->error("invalid compact id for class: already assigned to class `" + typeId + "'");
        }
    }

    ClassIdTokPtr classId = new ClassIdTok();
    classId->v = StringTokPtr::dynamicCast($2)->v;
    classId->t = static_cast<int>(id);
    $$ = classId;
}
| ICE_CLASS ICE_IDENT_OPEN scoped_name ')'
{
    StringTokPtr scoped = StringTokPtr::dynamicCast($3);

    ContainerPtr cont = unit->currentContainer();
    assert(cont);
    ContainedList cl = cont->lookupContained(scoped->v, false);
    if(cl.empty())
    {
        EnumeratorList enumerators = cont->enumerators(scoped->v);
        if(enumerators.size() == 1)
        {
            // Found
            cl.push_back(enumerators.front());
            scoped->v = enumerators.front()->scoped();
            unit->warning(Deprecated, string("referencing enumerator `") + scoped->v
                          + "' without its enumeration's scope is deprecated");
        }
        else if(enumerators.size() > 1)
        {
            ostringstream os;
            os << "enumerator `" << scoped->v << "' could designate";
            bool first = true;
            for(EnumeratorList::iterator p = enumerators.begin(); p != enumerators.end(); ++p)
            {
                if(first)
                {
                    first = false;
                }
                else
                {
                    os << " or";
                }

                os << " `" << (*p)->scoped() << "'";
            }
            unit->error(os.str());
        }
        else
        {
            unit->error(string("`") + scoped->v + "' is not defined");
        }
    }

    if(cl.empty())
    {
        YYERROR; // Can't continue, jump to next yyerrok
    }
    cont->checkIntroduced(scoped->v);

    int id = -1;
    EnumeratorPtr enumerator = EnumeratorPtr::dynamicCast(cl.front());
    ConstPtr constant = ConstPtr::dynamicCast(cl.front());
    if(constant)
    {
        BuiltinPtr b = BuiltinPtr::dynamicCast(constant->type());
        if(b && b->isIntegralType())
        {
            IceUtil::Int64 l = IceUtilInternal::strToInt64(constant->value().c_str(), 0, 0);
            if(l < 0 || l > INT32_MAX)
            {
                unit->error("compact id for class is out of range");
            }
            id = static_cast<int>(l);
        }
    }
    else if(enumerator)
    {
        id = enumerator->value();
    }

    if(id < 0)
    {
        unit->error("invalid compact id for class: id must be a positive integer");
    }
    else
    {
        string typeId = unit->getTypeId(id);
        if(!typeId.empty())
        {
            unit->error("invalid compact id for class: already assigned to class `" + typeId + "'");
        }
    }

    ClassIdTokPtr classId = new ClassIdTok();
    classId->v = StringTokPtr::dynamicCast($2)->v;
    classId->t = id;
    $$ = classId;

}
| class_name
{
    ClassIdTokPtr classId = new ClassIdTok();
    classId->v = StringTokPtr::dynamicCast($1)->v;
    classId->t = -1;
    $$ = classId;
}
;

// ----------------------------------------------------------------------
class_decl
// ----------------------------------------------------------------------
: class_name
{
    StringTokPtr ident = StringTokPtr::dynamicCast($1);
    ModulePtr cont = unit->currentModule();
    ClassDeclPtr cl = cont->createClassDecl(ident->v);
    $$ = cl;
}
;

// ----------------------------------------------------------------------
class_def
// ----------------------------------------------------------------------
: class_id class_extends
{
    ClassIdTokPtr ident = ClassIdTokPtr::dynamicCast($1);
    ModulePtr cont = unit->currentModule();
    ClassDefPtr base = ClassDefPtr::dynamicCast($2);
    ClassDefPtr cl = cont->createClassDef(ident->v, ident->t, base);
    if(cl)
    {
        cont->checkIntroduced(ident->v, cl);
        unit->pushContainer(cl);
        $$ = cl;
    }
    else
    {
        $$ = 0;
    }
}
'{' data_members '}'
{
    if($3)
    {
        unit->popContainer();
        $$ = $3;
    }
    else
    {
        $$ = 0;
    }
}
;

// ----------------------------------------------------------------------
class_extends
// ----------------------------------------------------------------------
: extends scoped_name
{
    StringTokPtr scoped = StringTokPtr::dynamicCast($2);
    ContainerPtr cont = unit->currentContainer();
    TypeList types = cont->lookupType(scoped->v);
    $$ = 0;
    if(!types.empty())
    {
        ClassDeclPtr cl = ClassDeclPtr::dynamicCast(types.front());
        if(!cl)
        {
            string msg = "`";
            msg += scoped->v;
            msg += "' is not a class";
            unit->error(msg);
        }
        else
        {
            ClassDefPtr def = cl->definition();
            if(!def)
            {
                string msg = "`";
                msg += scoped->v;
                msg += "' has been declared but not defined";
                unit->error(msg);
            }
            else
            {
                cont->checkIntroduced(scoped->v);
                $$ = def;
            }
        }
    }
}
| %empty
{
    $$ = 0;
}
;

// ----------------------------------------------------------------------
extends
// ----------------------------------------------------------------------
: ICE_EXTENDS
| ':'
;

// ----------------------------------------------------------------------
data_member
// ----------------------------------------------------------------------
: member
{
    TaggedDefTokPtr def = TaggedDefTokPtr::dynamicCast($1);

    // Check if the container was created successfully. If it wasn't skip creating the data member and continue parsing.
    if (DataMemberContainerPtr cont = DataMemberContainerPtr::dynamicCast(unit->currentContainer()))
    {
        MemberPtr dm = cont->createDataMember(def->name, def->type, def->isTagged, def->tag);
        unit->currentContainer()->checkIntroduced(def->name, dm);
        if (dm && !def->metadata.empty())
        {
            dm->setMetaData(def->metadata);
        }
    }
}
| member '=' const_initializer
{
    TaggedDefTokPtr def = TaggedDefTokPtr::dynamicCast($1);
    ConstDefTokPtr value = ConstDefTokPtr::dynamicCast($3);

    // Check if the container was created successfully. If it wasn't skip creating the data member and continue parsing.
    if (DataMemberContainerPtr cont = DataMemberContainerPtr::dynamicCast(unit->currentContainer()))
    {
        MemberPtr dm = cont->createDataMember(def->name, def->type, def->isTagged, def->tag, value->v,
                                              value->valueAsString, value->valueAsLiteral);
        unit->currentContainer()->checkIntroduced(def->name, dm);
        if (dm && !def->metadata.empty())
        {
            dm->setMetaData(def->metadata);
        }
    }
}
;

// ----------------------------------------------------------------------
data_member_list
// ----------------------------------------------------------------------
: data_member ';'
| error ';'
| data_member
{
    unit->error("`;' missing after definition");
}
| data_member_list data_member_list
;

// ----------------------------------------------------------------------
data_members
// ----------------------------------------------------------------------
: data_member_list
| %empty
;

// ----------------------------------------------------------------------
return_tuple
// ----------------------------------------------------------------------
: out_qualifier member
{
    TaggedDefTokPtr returnMember = TaggedDefTokPtr::dynamicCast($2);
    if (BoolTokPtr::dynamicCast($1)->v)
    {
        unit->error("`" + returnMember->name + "': return members cannot be marked as out");
    }

    TaggedDefListTokPtr returnMembers = new TaggedDefListTok();
    returnMembers->v.push_back(returnMember);
    $$ = returnMembers;
}
| return_tuple ',' out_qualifier member
{
    TaggedDefTokPtr returnMember = TaggedDefTokPtr::dynamicCast($4);
    if (BoolTokPtr::dynamicCast($3)->v)
    {
        unit->error("`" + returnMember->name + "': return members cannot be marked as out");
    }

    TaggedDefListTokPtr returnMembers = TaggedDefListTokPtr::dynamicCast($1);
    returnMembers->v.push_back(returnMember);
    $$ = returnMembers;
}

// ----------------------------------------------------------------------
return_type
// ----------------------------------------------------------------------
: tagged_type
{
    TaggedDefTokPtr returnMember = TaggedDefTokPtr::dynamicCast($1);
    // For unnamed return types we infer their name to be 'returnValue'.
    returnMember->name = "returnValue";

    if (returnMember->isTagged)
    {
        checkForTaggableType(returnMember->type);
    }

    TaggedDefListTokPtr returnMembers = new TaggedDefListTok();
    returnMembers->v.push_back(returnMember);
    $$ = returnMembers;
}
| '(' return_tuple ')'
{
    TaggedDefListTokPtr returnMembers = TaggedDefListTokPtr::dynamicCast($2);
    if (returnMembers->v.size() == 1)
    {
        unit->error("return tuples must contain at least 2 elements");
    }
    $$ = $2;
}
| '(' ')'
{
    unit->error("return tuples must contain at least 2 elements");
    $$ = new TaggedDefListTok();
}
| ICE_VOID
{
    $$ = new TaggedDefListTok();
}
;

// ----------------------------------------------------------------------
operation_preamble
// ----------------------------------------------------------------------
: return_type ICE_IDENT_OPEN
{
    TaggedDefListTokPtr returnMembers = TaggedDefListTokPtr::dynamicCast($1);
    string name = StringTokPtr::dynamicCast($2)->v;

    if (InterfaceDefPtr interface = InterfaceDefPtr::dynamicCast(unit->currentContainer()))
    {
        if (OperationPtr op = interface->createOperation(name))
        {
            interface->checkIntroduced(name, op);
            unit->pushContainer(op);

            // Set the return members for the operation.
            for (const auto& returnMember : returnMembers->v)
            {
                MemberPtr p = op->createReturnMember(returnMember->name, returnMember->type, returnMember->isTagged,
                                                     returnMember->tag);
                if (p && !returnMember->metadata.empty())
                {
                    p->setMetaData(returnMember->metadata);
                }
            }

            $$ = op;
        }
        else
        {
            $$ = 0;
        }
    }
    else
    {
        $$ = 0;
    }
}
| ICE_IDEMPOTENT return_type ICE_IDENT_OPEN
{
    TaggedDefListTokPtr returnMembers = TaggedDefListTokPtr::dynamicCast($2);
    string name = StringTokPtr::dynamicCast($3)->v;

    if (InterfaceDefPtr interface = InterfaceDefPtr::dynamicCast(unit->currentContainer()))
    {
        if (OperationPtr op = interface->createOperation(name, Operation::Idempotent))
        {
            interface->checkIntroduced(name, op);
            unit->pushContainer(op);

            // Set the return members for the operation.
            for (const auto& returnMember : returnMembers->v)
            {
                MemberPtr p = op->createReturnMember(returnMember->name, returnMember->type, returnMember->isTagged,
                                                     returnMember->tag);
                if (p && !returnMember->metadata.empty())
                {
                    p->setMetaData(returnMember->metadata);
                }
            }

            $$ = op;
        }
        else
        {
            $$ = 0;
        }
    }
    else
    {
        $$ = 0;
    }
}
| return_type ICE_KEYWORD_OPEN
{
    TaggedDefListTokPtr returnMembers = TaggedDefListTokPtr::dynamicCast($1);
    string name = StringTokPtr::dynamicCast($2)->v;
    if (InterfaceDefPtr interface = InterfaceDefPtr::dynamicCast(unit->currentContainer()))
    {
        if (OperationPtr op = interface->createOperation(name))
        {
            unit->pushContainer(op);

            // Set the return members for the operation.
            for (const auto& returnMember : returnMembers->v)
            {
                MemberPtr p = op->createReturnMember(returnMember->name, returnMember->type, returnMember->isTagged,
                                                     returnMember->tag);
                if (p && !returnMember->metadata.empty())
                {
                    p->setMetaData(returnMember->metadata);
                }
            }

            unit->error("keyword `" + name + "' cannot be used as operation name");
            $$ = op; // Dummy
        }
        else
        {
            $$ = 0;
        }
    }
    else
    {
        $$ = 0;
    }
}
| ICE_IDEMPOTENT return_type ICE_KEYWORD_OPEN
{
    TaggedDefListTokPtr returnMembers = TaggedDefListTokPtr::dynamicCast($2);
    string name = StringTokPtr::dynamicCast($3)->v;
    if (InterfaceDefPtr interface = InterfaceDefPtr::dynamicCast(unit->currentContainer()))
    {
        if (OperationPtr op = interface->createOperation(name, Operation::Idempotent))
        {
            unit->pushContainer(op);

            // Set the return members for the operation.
            for (const auto& returnMember : returnMembers->v)
            {
                MemberPtr p = op->createReturnMember(returnMember->name, returnMember->type, returnMember->isTagged,
                                                     returnMember->tag);
                if (p && !returnMember->metadata.empty())
                {
                    p->setMetaData(returnMember->metadata);
                }
            }

            unit->error("keyword `" + name + "' cannot be used as operation name");
            $$ = op; // Dummy
        }
        else
        {
            $$ = 0;
        }
    }
    else
    {
        $$ = 0;
    }
}
;

// ----------------------------------------------------------------------
operation
// ----------------------------------------------------------------------
: operation_preamble parameters ')'
{
    if($1)
    {
        unit->popContainer();
        $$ = $1;
    }
    else
    {
        $$ = 0;
    }
}
throws
{
    OperationPtr op = OperationPtr::dynamicCast($4);
    ExceptionListTokPtr el = ExceptionListTokPtr::dynamicCast($5);
    assert(el);
    if(op)
    {
        op->setExceptionList(el->v);
    }
}
| operation_preamble error ')'
{
    if($1)
    {
        unit->popContainer();
    }
    yyerrok;
}
throws
{
    OperationPtr op = OperationPtr::dynamicCast($4);
    ExceptionListTokPtr el = ExceptionListTokPtr::dynamicCast($5);
    assert(el);
    if(op)
    {
        op->setExceptionList(el->v); // Dummy
    }
}
;

// ----------------------------------------------------------------------
operation_list
// ----------------------------------------------------------------------
: local_metadata operation ';' operation_list
{
    StringListTokPtr metaData = StringListTokPtr::dynamicCast($1);
    OperationPtr operation = OperationPtr::dynamicCast($2);
    if (operation && !metaData->v.empty())
    {
        operation->setMetaData(metaData->v);

        // If the operation had a single return type (not a return tuple), also apply the metadata to the return type.
        // TODO: once we introduce more concrete metadata validation, we could sort the metadata out between the return
        // type and the operation itself. So metadata relevant to operations would only be set for the operation, and
        // metadata only relevant to the return type would only be set on the return type.
        if (operation->hasSingleReturnType())
        {
            operation->returnValues().front()->setMetaData(metaData->v);
        }
    }
}
| local_metadata operation
{
    unit->error("`;' missing after definition");
}
| error ';' operation_list
| %empty
;

// ----------------------------------------------------------------------
interface_id
// ----------------------------------------------------------------------
: ICE_INTERFACE ICE_IDENTIFIER
{
    $$ = $2;
}
| ICE_INTERFACE keyword
{
    StringTokPtr ident = StringTokPtr::dynamicCast($2);
    unit->error("keyword `" + ident->v + "' cannot be used as interface name");
    $$ = $2; // Dummy
}
;

// ----------------------------------------------------------------------
interface_decl
// ----------------------------------------------------------------------
: interface_id
{
    StringTokPtr ident = StringTokPtr::dynamicCast($1);
    ModulePtr cont = unit->currentModule();
    InterfaceDeclPtr cl = cont->createInterfaceDecl(ident->v);
    cont->checkIntroduced(ident->v, cl);
    $$ = cl;
}
;

// ----------------------------------------------------------------------
interface_def
// ----------------------------------------------------------------------
: interface_id interface_extends
{
    StringTokPtr ident = StringTokPtr::dynamicCast($1);
    ModulePtr cont = unit->currentModule();
    InterfaceListTokPtr bases = InterfaceListTokPtr::dynamicCast($2);
    InterfaceDefPtr interface = cont->createInterfaceDef(ident->v, bases->v);
    if(interface)
    {
        cont->checkIntroduced(ident->v, interface);
        unit->pushContainer(interface);
        $$ = interface;
    }
    else
    {
        $$ = 0;
    }
}
'{' operation_list '}'
{
    if($3)
    {
        unit->popContainer();
        $$ = $3;
    }
    else
    {
        $$ = 0;
    }
}
;

// ----------------------------------------------------------------------
interface_list
// ----------------------------------------------------------------------
: scoped_name ',' interface_list
{
    InterfaceListTokPtr intfs = InterfaceListTokPtr::dynamicCast($3);
    StringTokPtr scoped = StringTokPtr::dynamicCast($1);
    ContainerPtr cont = unit->currentContainer();
    TypeList types = cont->lookupType(scoped->v);
    if(!types.empty())
    {
        InterfaceDeclPtr interface = InterfaceDeclPtr::dynamicCast(types.front());
        if(!interface)
        {
            string msg = "`";
            msg += scoped->v;
            msg += "' is not an interface";
            unit->error(msg);
        }
        else
        {
            InterfaceDefPtr def = interface->definition();
            if(!def)
            {
                string msg = "`";
                msg += scoped->v;
                msg += "' has been declared but not defined";
                unit->error(msg);
            }
            else
            {
                cont->checkIntroduced(scoped->v);
                intfs->v.push_front(def);
            }
        }
    }
    $$ = intfs;
}
| scoped_name
{
    InterfaceListTokPtr intfs = new InterfaceListTok;
    StringTokPtr scoped = StringTokPtr::dynamicCast($1);
    ContainerPtr cont = unit->currentContainer();
    TypeList types = cont->lookupType(scoped->v);
    if(!types.empty())
    {
        InterfaceDeclPtr interface = InterfaceDeclPtr::dynamicCast(types.front());
        if(!interface)
        {
            string msg = "`";
            msg += scoped->v;
            msg += "' is not an interface";
            unit->error(msg); // $$ is a dummy
        }
        else
        {
            InterfaceDefPtr def = interface->definition();
            if(!def)
            {
                string msg = "`";
                msg += scoped->v;
                msg += "' has been declared but not defined";
                unit->error(msg); // $$ is a dummy
            }
            else
            {
                cont->checkIntroduced(scoped->v);
                intfs->v.push_front(def);
            }
        }
    }
    $$ = intfs;
}
| ICE_OBJECT
{
    unit->error("illegal inheritance from type Object");
    $$ = new InterfaceListTok; // Dummy
}
| ICE_ANYCLASS
{
    unit->error("illegal inheritance from type AnyClass");
    $$ = new ClassListTok; // Dummy
}
| ICE_VALUE
{
    if (!unit->compatMode())
    {
        unit->warning(Deprecated, "the `Value' keyword is deprecated, use `AnyClass' instead");
    }
    unit->error("illegal inheritance from type Value");
    $$ = new ClassListTok; // Dummy
}
;

// ----------------------------------------------------------------------
interface_extends
// ----------------------------------------------------------------------
: extends interface_list
{
    $$ = $2;
}
| %empty
{
    $$ = new InterfaceListTok;
}
;

// ----------------------------------------------------------------------
exception_list
// ----------------------------------------------------------------------
: exception ',' exception_list
{
    ExceptionPtr exception = ExceptionPtr::dynamicCast($1);
    ExceptionListTokPtr exceptionList = ExceptionListTokPtr::dynamicCast($3);
    exceptionList->v.push_front(exception);
    $$ = exceptionList;
}
| exception
{
    ExceptionPtr exception = ExceptionPtr::dynamicCast($1);
    ExceptionListTokPtr exceptionList = new ExceptionListTok;
    exceptionList->v.push_front(exception);
    $$ = exceptionList;
}
;

// ----------------------------------------------------------------------
exception
// ----------------------------------------------------------------------
: scoped_name
{
    StringTokPtr scoped = StringTokPtr::dynamicCast($1);
    ContainerPtr cont = unit->currentContainer();
    ExceptionPtr exception = cont->lookupException(scoped->v);
    if(!exception)
    {
        exception = unit->currentModule()->createException(IceUtil::generateUUID(), 0, Dummy); // Dummy
    }
    cont->checkIntroduced(scoped->v, exception);
    $$ = exception;
}
| keyword
{
    StringTokPtr ident = StringTokPtr::dynamicCast($1);
    unit->error("keyword `" + ident->v + "' cannot be used as exception name");
    $$ = unit->currentModule()->createException(IceUtil::generateUUID(), 0, Dummy); // Dummy
}
;

// ----------------------------------------------------------------------
sequence_def
// ----------------------------------------------------------------------
: ICE_SEQUENCE '<' local_metadata type '>' ICE_IDENTIFIER
{
    StringTokPtr ident = StringTokPtr::dynamicCast($6);
    StringListTokPtr metaData = StringListTokPtr::dynamicCast($3);
    TypePtr type = TypePtr::dynamicCast($4);
    ModulePtr cont = unit->currentModule();
    $$ = cont->createSequence(ident->v, type, metaData->v);
}
| ICE_SEQUENCE '<' local_metadata type '>' keyword
{
    StringTokPtr ident = StringTokPtr::dynamicCast($6);
    StringListTokPtr metaData = StringListTokPtr::dynamicCast($3);
    TypePtr type = TypePtr::dynamicCast($4);
    ModulePtr cont = unit->currentModule();
    $$ = cont->createSequence(ident->v, type, metaData->v); // Dummy
    unit->error("keyword `" + ident->v + "' cannot be used as sequence name");
}
;

// ----------------------------------------------------------------------
dictionary_def
// ----------------------------------------------------------------------
: ICE_DICTIONARY '<' local_metadata type ',' local_metadata type '>' ICE_IDENTIFIER
{
    StringTokPtr ident = StringTokPtr::dynamicCast($9);
    StringListTokPtr keyMetaData = StringListTokPtr::dynamicCast($3);
    TypePtr keyType = TypePtr::dynamicCast($4);
    StringListTokPtr valueMetaData = StringListTokPtr::dynamicCast($6);
    TypePtr valueType = TypePtr::dynamicCast($7);
    ModulePtr cont = unit->currentModule();
    $$ = cont->createDictionary(ident->v, keyType, keyMetaData->v, valueType, valueMetaData->v);
}
| ICE_DICTIONARY '<' local_metadata type ',' local_metadata type '>' keyword
{
    StringTokPtr ident = StringTokPtr::dynamicCast($9);
    StringListTokPtr keyMetaData = StringListTokPtr::dynamicCast($3);
    TypePtr keyType = TypePtr::dynamicCast($4);
    StringListTokPtr valueMetaData = StringListTokPtr::dynamicCast($6);
    TypePtr valueType = TypePtr::dynamicCast($7);
    ModulePtr cont = unit->currentModule();
    $$ = cont->createDictionary(ident->v, keyType, keyMetaData->v, valueType, valueMetaData->v); // Dummy
    unit->error("keyword `" + ident->v + "' cannot be used as dictionary name");
}
;

// ----------------------------------------------------------------------
enum_start
// ----------------------------------------------------------------------
: ICE_UNCHECKED ICE_ENUM
{
    $$ = new BoolTok(true);
}
| ICE_ENUM
{
    $$ = new BoolTok(false);
}
;

// ----------------------------------------------------------------------
enum_id
// ----------------------------------------------------------------------
: enum_start ICE_IDENTIFIER
{
    bool unchecked = BoolTokPtr::dynamicCast($1)->v;
    StringTokPtr ident = StringTokPtr::dynamicCast($2);
    ModulePtr cont = unit->currentModule();
    EnumPtr en = cont->createEnum(ident->v, unchecked);
    if (en)
    {
        cont->checkIntroduced(ident->v, en);
    }
    else
    {
        en = cont->createEnum(IceUtil::generateUUID(), unchecked, Dummy);
    }
    $$ = en;
}
| enum_start keyword
{
    bool unchecked = BoolTokPtr::dynamicCast($1)->v;
    StringTokPtr ident = StringTokPtr::dynamicCast($2);
    ModulePtr cont = unit->currentModule();
    unit->error("keyword `" + ident->v + "' cannot be used as enumeration name");
    $$ = cont->createEnum(IceUtil::generateUUID(), unchecked, Dummy);
}
;

// ----------------------------------------------------------------------
enum_def
// ----------------------------------------------------------------------
: enum_id enum_underlying
{
    EnumPtr en = EnumPtr::dynamicCast($1);
    en->initUnderlying(TypePtr::dynamicCast($2));
    unit->pushContainer(en);
    $$ = en;
}
'{' enumerator_list '}'
{
    EnumPtr en = EnumPtr::dynamicCast($3);
    if(en)
    {
        EnumeratorListTokPtr enumerators = EnumeratorListTokPtr::dynamicCast($5);
        if(enumerators->v.empty())
        {
            unit->error("enum `" + en->name() + "' must have at least one enumerator");
        }
        unit->popContainer();
    }
    $$ = $3;
}
|
enum_start
{
    bool unchecked = BoolTokPtr::dynamicCast($1)->v;
    unit->error("missing enumeration name");
    ModulePtr cont = unit->currentModule();
    EnumPtr en = cont->createEnum(IceUtil::generateUUID(), unchecked, Dummy);
    unit->pushContainer(en);
    $$ = en;
}
'{' enumerator_list '}'
{
    unit->popContainer();
    $$ = $1;
}
;

// ----------------------------------------------------------------------
enum_underlying
// ----------------------------------------------------------------------
: ':' type
{
    $$ = $2;
}
| %empty
{
    $$ = 0;
}
;

// ----------------------------------------------------------------------
enumerator_list
// ----------------------------------------------------------------------
: enumerator ',' enumerator_list
{
    EnumeratorListTokPtr ens = EnumeratorListTokPtr::dynamicCast($1);
    ens->v.splice(ens->v.end(), EnumeratorListTokPtr::dynamicCast($3)->v);
    $$ = ens;
}
| enumerator
;

// ----------------------------------------------------------------------
enumerator
// ----------------------------------------------------------------------
: ICE_IDENTIFIER
{
    StringTokPtr ident = StringTokPtr::dynamicCast($1);
    EnumeratorListTokPtr ens = new EnumeratorListTok;
    EnumPtr cont = EnumPtr::dynamicCast(unit->currentContainer());
    EnumeratorPtr en = cont->createEnumerator(ident->v);
    if(en)
    {
        ens->v.push_front(en);
    }
    $$ = ens;
}
| ICE_IDENTIFIER '=' enumerator_initializer
{
    StringTokPtr ident = StringTokPtr::dynamicCast($1);
    EnumeratorListTokPtr ens = new EnumeratorListTok;
    EnumPtr cont = EnumPtr::dynamicCast(unit->currentContainer());
    IntegerTokPtr intVal = IntegerTokPtr::dynamicCast($3);
    if (intVal)
    {
       EnumeratorPtr en = cont->createEnumerator(ident->v, intVal->v);
       ens->v.push_front(en);
    }
    $$ = ens;
}
| keyword
{
    StringTokPtr ident = StringTokPtr::dynamicCast($1);
    unit->error("keyword `" + ident->v + "' cannot be used as enumerator");
    EnumeratorListTokPtr ens = new EnumeratorListTok; // Dummy
    $$ = ens;
}
| %empty
{
    EnumeratorListTokPtr ens = new EnumeratorListTok;
    $$ = ens; // Dummy
}
;

// ----------------------------------------------------------------------
enumerator_initializer
// ----------------------------------------------------------------------
: ICE_INTEGER_LITERAL
{
    $$ = $1;
}
| scoped_name
{
    StringTokPtr scoped = StringTokPtr::dynamicCast($1);
    ContainedList cl = unit->currentContainer()->lookupContained(scoped->v);
    IntegerTokPtr tok;
    if(!cl.empty())
    {
        ConstPtr constant = ConstPtr::dynamicCast(cl.front());
        if(constant)
        {
            unit->currentContainer()->checkIntroduced(scoped->v, constant);
            BuiltinPtr b = BuiltinPtr::dynamicCast(constant->type());
            if(b && b->isIntegralType())
            {
                IceUtil::Int64 v;
                if(IceUtilInternal::stringToInt64(constant->value(), v))
                {
                    tok = new IntegerTok;
                    tok->v = v;
                    tok->literal = constant->value();
                }
            }
        }
    }

    if(!tok)
    {
        string msg = "illegal initializer: `" + scoped->v + "' is not an integer constant";
        unit->error(msg); // $$ is dummy
    }

    $$ = tok;
}
;

// ----------------------------------------------------------------------
out_qualifier
// ----------------------------------------------------------------------
: ICE_OUT
{
    $$ = new BoolTok(true);
}
| %empty
{
    $$ = new BoolTok(false);
}
;

// ----------------------------------------------------------------------
parameter
// ----------------------------------------------------------------------
: out_qualifier member
{
    BoolTokPtr isOutParam = BoolTokPtr::dynamicCast($1);
    TaggedDefTokPtr def = TaggedDefTokPtr::dynamicCast($2);

    if (OperationPtr op = OperationPtr::dynamicCast(unit->currentContainer()))
    {
        MemberPtr param = op->createParameter(def->name, def->type, isOutParam->v, def->isTagged, def->tag);
        unit->currentContainer()->checkIntroduced(def->name, param);
        if(param && !def->metadata.empty())
        {
            param->setMetaData(def->metadata);
        }
    }
}

// ----------------------------------------------------------------------
parameter_list
// ----------------------------------------------------------------------
: parameter
| parameter_list ',' parameter
;

// ----------------------------------------------------------------------
parameters
// ----------------------------------------------------------------------
: parameter_list
| %empty
;

// ----------------------------------------------------------------------
throws
// ----------------------------------------------------------------------
: ICE_THROWS exception_list
{
    $$ = $2;
}
| %empty
{
    $$ = new ExceptionListTok;
}
;

// ----------------------------------------------------------------------
scoped_name
// ----------------------------------------------------------------------
: ICE_IDENTIFIER
| ICE_SCOPED_IDENTIFIER
;

// ----------------------------------------------------------------------
builtin
// ----------------------------------------------------------------------
: ICE_BOOL {}
| ICE_BYTE {}
| ICE_SHORT {}
| ICE_USHORT {}
| ICE_INT {}
| ICE_UINT {}
| ICE_VARINT {}
| ICE_VARUINT {}
| ICE_LONG {}
| ICE_ULONG {}
| ICE_VARLONG {}
| ICE_VARULONG {}
| ICE_FLOAT {}
| ICE_DOUBLE {}
| ICE_STRING {}

// ----------------------------------------------------------------------
type
// ----------------------------------------------------------------------
: ICE_OBJECT '*'
{
    $$ = unit->optionalBuiltin(Builtin::KindObject);
}
| ICE_OBJECT '?'
{
    $$ = unit->optionalBuiltin(Builtin::KindObject);
}
| ICE_ANYCLASS '?'
{
    $$ = unit->optionalBuiltin(Builtin::KindAnyClass);
}
| ICE_VALUE '?'
{
    if (!unit->compatMode())
    {
        unit->warning(Deprecated, "the `Value' keyword is deprecated, use `AnyClass' instead");
    }
    $$ = unit->optionalBuiltin(Builtin::KindAnyClass);
}
| builtin '?'
{
    StringTokPtr typeName = StringTokPtr::dynamicCast($1);
    $$ = unit->optionalBuiltin(Builtin::kindFromString(typeName->v).value());
}
| ICE_OBJECT
{
    if (unit->compatMode())
    {
        $$ = unit->optionalBuiltin(Builtin::KindAnyClass);
    }
    else
    {
        $$ = unit->builtin(Builtin::KindObject);
    }
}
| ICE_ANYCLASS
{
    $$ = unit->builtin(Builtin::KindAnyClass);
}
| ICE_VALUE
{
    if (unit->compatMode())
    {
        $$ = unit->optionalBuiltin(Builtin::KindAnyClass);
    }
    else
    {
        unit->warning(Deprecated, "the `Value' keyword is deprecated, use `AnyClass' instead");
        $$ = unit->builtin(Builtin::KindAnyClass);
    }
}
| builtin
{
    StringTokPtr typeName = StringTokPtr::dynamicCast($1);
    $$ = unit->builtin(Builtin::kindFromString(typeName->v).value());
}
| scoped_name
{
    StringTokPtr scoped = StringTokPtr::dynamicCast($1);
    ContainerPtr cont = unit->currentContainer();
    if(cont)
    {
        TypeList types = cont->lookupType(scoped->v);
        if(types.empty())
        {
            YYERROR; // Can't continue, jump to next yyerrok
        }
        cont->checkIntroduced(scoped->v);
        if (ClassDeclPtr::dynamicCast(types.front()) && unit->compatMode())
        {
            $$ = new Optional(types.front());
        }
        else
        {
            $$ = types.front();
        }
    }
    else
    {
        $$ = 0;
    }
}
| scoped_name '*'
{
    StringTokPtr scoped = StringTokPtr::dynamicCast($1);
    ContainerPtr cont = unit->currentContainer();
    if(cont)
    {
        TypeList types = cont->lookupType(scoped->v);
        if(types.empty())
        {
            YYERROR; // Can't continue, jump to next yyerrok
        }
        for(TypeList::iterator p = types.begin(); p != types.end(); ++p)
        {
            InterfaceDeclPtr interface = InterfaceDeclPtr::dynamicCast(*p);
            if(!interface)
            {
                string msg = "`";
                msg += scoped->v;
                msg += "' must be an interface";
                unit->error(msg);
                YYERROR; // Can't continue, jump to next yyerrok
            }
            cont->checkIntroduced(scoped->v);
            *p = new Optional(interface);
        }
        $$ = types.front();
    }
    else
    {
        $$ = 0;
    }
}
| scoped_name '?'
{
    StringTokPtr scoped = StringTokPtr::dynamicCast($1);
    ContainerPtr cont = unit->currentContainer();
    if(cont)
    {
        TypeList types = cont->lookupType(scoped->v);
        if(types.empty())
        {
            YYERROR; // Can't continue, jump to next yyerrok
        }
        for(TypeList::iterator p = types.begin(); p != types.end(); ++p)
        {
            cont->checkIntroduced(scoped->v);
            *p = new Optional(*p);
        }
        $$ = types.front();
    }
    else
    {
        $$ = 0;
    }
}
;

// ----------------------------------------------------------------------
tagged_type
// ----------------------------------------------------------------------
: tag type
{
    TaggedDefTokPtr taggedDef = TaggedDefTokPtr::dynamicCast($1);
    OptionalPtr type = OptionalPtr::dynamicCast($2);

    if (!type)
    {
        // Infer the type to be optional so parsing can continue without nullptrs.
        type = new Optional(TypePtr::dynamicCast($2));
        unit->error("only optional types can be tagged");
    }

    taggedDef->type = type;
    $$ = taggedDef;
}
| optional type
{
    TaggedDefTokPtr taggedDef = TaggedDefTokPtr::dynamicCast($1);
    OptionalPtr type = OptionalPtr::dynamicCast($2);

    if (!type)
    {
        // Infer the type to be optional for backwards compatibility.
        type = new Optional(TypePtr::dynamicCast($2));
    }

    taggedDef->type = type;
    $$ = taggedDef;
}
| type
{
    TaggedDefTokPtr taggedDef = new TaggedDefTok;
    taggedDef->type = TypePtr::dynamicCast($1);
    $$ = taggedDef;
}
;

// ----------------------------------------------------------------------
member
// ----------------------------------------------------------------------
: tagged_type ICE_IDENTIFIER
{
    TaggedDefTokPtr def = TaggedDefTokPtr::dynamicCast($1);
    def->name = StringTokPtr::dynamicCast($2)->v;
    checkIdentifier(def->name);
    if (def->isTagged)
    {
        checkForTaggableType(def->type, def->name);
    }

    $$ = def;
}
| tagged_type keyword
{
    TaggedDefTokPtr def = TaggedDefTokPtr::dynamicCast($1);
    def->name = StringTokPtr::dynamicCast($2)->v;
    if (def->isTagged)
    {
        checkForTaggableType(def->type, def->name);
    }
    unit->error("keyword `" + def->name + "' cannot be used as an identifier");
    $$ = def;
}
| tagged_type
{
    TaggedDefTokPtr def = TaggedDefTokPtr::dynamicCast($1);
    def->name = IceUtil::generateUUID(); // Dummy
    if (def->isTagged)
    {
        checkForTaggableType(def->type);
    }
    unit->error("missing identifier");
    $$ = def;
}
| local_metadata member
{
    TaggedDefTokPtr def = TaggedDefTokPtr::dynamicCast($2);
    def->metadata = StringListTokPtr::dynamicCast($1)->v;
    $$ = def;
}
;

// ----------------------------------------------------------------------
string_literal
// ----------------------------------------------------------------------
: ICE_STRING_LITERAL string_literal // Adjacent string literals are concatenated
{
    StringTokPtr str1 = StringTokPtr::dynamicCast($1);
    StringTokPtr str2 = StringTokPtr::dynamicCast($2);
    str1->v += str2->v;
}
| ICE_STRING_LITERAL
;

// ----------------------------------------------------------------------
string_list
// ----------------------------------------------------------------------
: string_list ',' string_literal
{
    StringTokPtr str = StringTokPtr::dynamicCast($3);
    StringListTokPtr stringList = StringListTokPtr::dynamicCast($1);
    stringList->v.push_back(str->v);
    $$ = stringList;
}
| string_literal
{
    StringTokPtr str = StringTokPtr::dynamicCast($1);
    StringListTokPtr stringList = new StringListTok;
    stringList->v.push_back(str->v);
    $$ = stringList;
}
;

// ----------------------------------------------------------------------
const_initializer
// ----------------------------------------------------------------------
: ICE_INTEGER_LITERAL
{
    BuiltinPtr type = unit->builtin(Builtin::KindLong);
    IntegerTokPtr intVal = IntegerTokPtr::dynamicCast($1);
    ostringstream sstr;
    sstr << intVal->v;
    ConstDefTokPtr def = new ConstDefTok(type, sstr.str(), intVal->literal);
    $$ = def;
}
| ICE_FLOATING_POINT_LITERAL
{
    BuiltinPtr type = unit->builtin(Builtin::KindDouble);
    FloatingTokPtr floatVal = FloatingTokPtr::dynamicCast($1);
    ostringstream sstr;
    sstr << floatVal->v;
    ConstDefTokPtr def = new ConstDefTok(type, sstr.str(), floatVal->literal);
    $$ = def;
}
| scoped_name
{
    StringTokPtr scoped = StringTokPtr::dynamicCast($1);
    ConstDefTokPtr def;
    ContainedList cl = unit->currentContainer()->lookupContained(scoped->v, false);
    if(cl.empty())
    {
        // Could be an enumerator
        def = new ConstDefTok(SyntaxTreeBasePtr(0), scoped->v, scoped->v);
    }
    else
    {
        EnumeratorPtr enumerator = EnumeratorPtr::dynamicCast(cl.front());
        ConstPtr constant = ConstPtr::dynamicCast(cl.front());
        if(enumerator)
        {
            unit->currentContainer()->checkIntroduced(scoped->v, enumerator);
            def = new ConstDefTok(enumerator, scoped->v, scoped->v);
        }
        else if(constant)
        {
            unit->currentContainer()->checkIntroduced(scoped->v, constant);
            def = new ConstDefTok(constant, constant->value(), constant->value());
        }
        else
        {
            def = new ConstDefTok;
            string msg = "illegal initializer: `" + scoped->v + "' is a";
            static const string vowels = "aeiou";
            string kindOf = cl.front()->kindOf();
            if(vowels.find_first_of(kindOf[0]) != string::npos)
            {
                msg += "n";
            }
            msg += " " + kindOf;
            unit->error(msg); // $$ is dummy
        }
    }
    $$ = def;
}
| ICE_STRING_LITERAL
{
    BuiltinPtr type = unit->builtin(Builtin::KindString);
    StringTokPtr literal = StringTokPtr::dynamicCast($1);
    ConstDefTokPtr def = new ConstDefTok(type, literal->v, literal->literal);
    $$ = def;
}
| ICE_FALSE
{
    BuiltinPtr type = unit->builtin(Builtin::KindBool);
    StringTokPtr literal = StringTokPtr::dynamicCast($1);
    ConstDefTokPtr def = new ConstDefTok(type, "false", "false");
    $$ = def;
}
| ICE_TRUE
{
    BuiltinPtr type = unit->builtin(Builtin::KindBool);
    StringTokPtr literal = StringTokPtr::dynamicCast($1);
    ConstDefTokPtr def = new ConstDefTok(type, "true", "true");
    $$ = def;
}
;

// ----------------------------------------------------------------------
const_def
// ----------------------------------------------------------------------
: ICE_CONST local_metadata type ICE_IDENTIFIER '=' const_initializer
{
    StringListTokPtr metaData = StringListTokPtr::dynamicCast($2);
    TypePtr const_type = TypePtr::dynamicCast($3);
    StringTokPtr ident = StringTokPtr::dynamicCast($4);
    ConstDefTokPtr value = ConstDefTokPtr::dynamicCast($6);
    $$ = unit->currentModule()->createConst(ident->v, const_type, metaData->v, value->v,
                                               value->valueAsString, value->valueAsLiteral);
}
| ICE_CONST local_metadata type '=' const_initializer
{
    StringListTokPtr metaData = StringListTokPtr::dynamicCast($2);
    TypePtr const_type = TypePtr::dynamicCast($3);
    ConstDefTokPtr value = ConstDefTokPtr::dynamicCast($5);
    unit->error("missing constant name");
    $$ = unit->currentModule()->createConst(IceUtil::generateUUID(), const_type, metaData->v, value->v,
                                               value->valueAsString, value->valueAsLiteral, Dummy); // Dummy
}
;

// ----------------------------------------------------------------------
keyword
// ----------------------------------------------------------------------
: ICE_MODULE {}
| ICE_CLASS {}
| ICE_INTERFACE {}
| ICE_EXCEPTION {}
| ICE_STRUCT {}
| ICE_SEQUENCE {}
| ICE_DICTIONARY {}
| ICE_ENUM {}
| ICE_OUT {}
| ICE_EXTENDS {}
| ICE_IMPLEMENTS {}
| ICE_THROWS {}
| ICE_VOID {}
| ICE_BOOL {}
| ICE_BYTE {}
| ICE_SHORT {}
| ICE_USHORT {}
| ICE_INT {}
| ICE_UINT {}
| ICE_VARINT {}
| ICE_VARUINT {}
| ICE_LONG {}
| ICE_ULONG {}
| ICE_VARLONG {}
| ICE_VARULONG {}
| ICE_FLOAT {}
| ICE_DOUBLE {}
| ICE_STRING {}
| ICE_OBJECT {}
| ICE_CONST {}
| ICE_FALSE {}
| ICE_TRUE {}
| ICE_IDEMPOTENT {}
| ICE_TAG {}
| ICE_OPTIONAL {}
| ICE_ANYCLASS {}
| ICE_VALUE {}
;

%%
